import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.JPanel;

/**
 * 
 */

/**
 * @author Abubakr
 * 
 */
public class Board extends JPanel implements KeyListener {

	private static final long serialVersionUID = -6800523969651961878L;

	/**
	 * The size of one block. The entire board is comprised of 21 blocks.
	 */
	final int size = 500 / 21;
	/**
	 * The given map. b = black space w = wall t = thing
	 */
	final char[][] map = {
			{ 'b', 'w', 'w', 'w', 'w', 'w', 'w', 'w', 'w', 'w', 'w', 'w', 'w',
					'w', 'w', 'w', 'w', 'w', 'w', 'w', 'b' },
			{ 'b', 'w', 't', 't', 't', 't', 't', 't', 't', 't', 'w', 't', 't',
					't', 't', 't', 't', 't', 't', 'w', 'b' },
			{ 'b', 'w', 'p', 'w', 'w', 't', 'w', 'w', 'w', 't', 'w', 't', 'w',
					'w', 'w', 't', 'w', 'w', 'p', 'w', 'b' },
			{ 'b', 'w', 't', 't', 't', 't', 't', 't', 't', 't', 't', 't', 't',
					't', 't', 't', 't', 't', 't', 'w', 'b' },
			{ 'b', 'w', 't', 'w', 'w', 't', 'w', 't', 'w', 'w', 'w', 'w', 'w',
					't', 'w', 't', 'w', 'w', 't', 'w', 'b' },
			{ 'b', 'w', 't', 't', 't', 't', 'w', 't', 't', 't', 'w', 't', 't',
					't', 'w', 't', 't', 't', 't', 'w', 'b' },
			{ 'b', 'w', 'w', 'w', 'w', 't', 'w', 'w', 'w', 'b', 'w', 'b', 'w',
					'w', 'w', 't', 'w', 'w', 'w', 'w', 'b' },
			{ 'b', 'b', 'b', 'b', 'w', 't', 'w', 'b', 'b', 'b', 'b', 'b', 'b',
					'b', 'w', 't', 'w', 'b', 'b', 'b', 'b' },
			{ 'w', 'w', 'w', 'w', 'w', 't', 'w', 'b', 'w', 'w', 'w', 'w', 'w',
					'b', 'w', 't', 'w', 'w', 'w', 'w', 'w' },
			{ 'b', 'b', 'b', 'b', 'b', 't', 'b', 'b', 'w', 'b', 'b', 'b', 'w',
					'b', 'b', 't', 'b', 'b', 'b', 'b', 'b' },
			{ 'w', 'w', 'w', 'w', 'w', 't', 'w', 'b', 'w', 'w', 'w', 'w', 'w',
					'b', 'w', 't', 'w', 'w', 'w', 'w', 'w' },
			{ 'b', 'b', 'b', 'b', 'w', 't', 'w', 'b', 'b', 'b', 'b', 'b', 'b',
					'b', 'w', 't', 'w', 'b', 'b', 'b', 'b' },
			{ 'b', 'w', 'w', 'w', 'w', 't', 'w', 'b', 'w', 'w', 'w', 'w', 'w',
					'b', 'w', 't', 'w', 'w', 'w', 'w', 'b' },
			{ 'b', 'w', 't', 't', 't', 't', 't', 't', 't', 't', 'w', 't', 't',
					't', 't', 't', 't', 't', 't', 'w', 'b' },
			{ 'b', 'w', 't', 'w', 'w', 't', 'w', 'w', 'w', 't', 'w', 't', 'w',
					'w', 'w', 't', 'w', 'w', 't', 'w', 'b' },
			{ 'b', 'w', 'p', 't', 'w', 't', 't', 't', 't', 't', 't', 't', 't',
					't', 't', 't', 'w', 't', 'p', 'w', 'b' },
			{ 'b', 'w', 'w', 't', 'w', 't', 'w', 't', 'w', 'w', 'w', 'w', 'w',
					't', 'w', 't', 'w', 't', 'w', 'w', 'b' },
			{ 'b', 'w', 't', 't', 't', 't', 'w', 't', 't', 't', 'w', 't', 't',
					't', 't', 't', 't', 't', 't', 'w', 'b' },
			{ 'b', 'w', 't', 'w', 'w', 'w', 'w', 'w', 'w', 't', 'w', 't', 'w',
					'w', 'w', 'w', 'w', 'w', 't', 'w', 'b' },
			{ 'b', 'w', 't', 't', 't', 't', 't', 't', 't', 't', 't', 't', 't',
					't', 't', 't', 't', 't', 't', 'w', 'b' },
			{ 'b', 'w', 'w', 'w', 'w', 'w', 'w', 'w', 'w', 'w', 'w', 'w', 'w',
					'w', 'w', 'w', 'w', 'w', 'w', 'w', 'b' } };
	PacMan pacman = new PacMan();
	Ghost ghost = new Ghost();
	int px = 10, py = 15, dir = 0, speed = 4; // The x, y position of
												// the
	// PacMan, it's direction and
	// speed.
	int[] gx = { 10, 9, 10, 11 }, gy = { 7, 9, 9, 9 }; // The positions of each
														// ghost.
	Timer mover = new Timer(), eater = new Timer(); // Timers managing the
													// movement of the PacMan
													// and mouth animation.
	boolean mouth = false; // Determines if the mouth is open or closed.
	Timer ghosts = new Timer(); // Controls the movement of the ghosts.
	int[] gdir = {0,0,0,0};

	/**
	 * Creates a new board with a specified size. The given PacMan is then
	 * animated using two timers. One timer manages the x,y position while the
	 * other animates the mouth. The first timer also checks if the PacMan ate
	 * an object.
	 * 
	 * @param pacman
	 *            The PacMan Object
	 */
	public Board(PacMan pacman) {
		super();
		this.addKeyListener(this);
		Dimension preferredSize = new Dimension(500, 500);
		this.setPreferredSize(preferredSize);
		this.setFocusable(true);
		this.pacman = pacman;
		mover.schedule(new TimerTask() {
			@Override
			public void run() {
				if (dir == 0 && map[py - 1][px] != 'w') {
					py--;
				} else if (dir == 1) {
					if (px == 20) {
						px = 0;
					} else {
						if (map[py][px + 1] != 'w') {
							px++;
						}
					}
				} else if (dir == 2 && map[py + 1][px] != 'w') {
					py++;
				} else if (dir == 3) {
					if (px == 0) {
						px = 20;
					} else {
						if (map[py][px - 1] != 'w') {
							px--;
						}
					}
				}
				repaint();

			}

		}, 0, 1000 / speed);

		eater.schedule(new TimerTask() {

			@Override
			public void run() {
				if (mouth) {
					mouth = false;
				} else {
					mouth = true;
				}

				if (map[py][px] == 't') {
					map[py][px] = 'b';
				}
				repaint();
			}

		}, 0, 200);
		ghosts.schedule(new TimerTask() {
			@Override
			public void run() {
				// Randomly choose a direction when it hits a wall.
				int random = (int) ((Math.random() * 5) - 1);
				for (int i = 0; i < 4; i++) {
					if (gdir[i] == 0) {
						if (map[gy[i] - 1][gx[i]] != 'w') {
							gy[i]--;
						} else {
							gdir[i] = random;
						}
					} else if (gdir[i] == 1) {
						if (map[gy[i]][gx[i] + 1] != 'w') {
							gx[i]++;
						} else {
							gdir[i] = random;
						}

					} else if (gdir[i] == 2) {
						if (map[gy[i] + 1][gx[i]] != 'w') {
							gy[i]++;
						} else {
							gdir[i] = random;
						}

					} else if (gdir[i] == 3) {
						if (map[gy[i]][gx[i] - 1] != 'w') {
							gx[i]--;
						} else {
							gdir[i] = random;
						}
					}
				}
				repaint();
			}
		}, 0, 200);
	}

	@Override
	public void keyPressed(KeyEvent e) {
		int i = e.getKeyCode();
		if (i == KeyEvent.VK_UP) {
			dir = 0;
		} else if (i == KeyEvent.VK_RIGHT) {
			dir = 1;
		} else if (i == KeyEvent.VK_DOWN) {
			dir = 2;
		} else if (i == KeyEvent.VK_LEFT) {
			dir = 3;
		}
		this.repaint();
	}

	@Override
	public void keyReleased(KeyEvent e) {

	}

	@Override
	public void keyTyped(KeyEvent e) {

	}

	@Override
	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		drawMaze(g);
	}

	/**
	 * Draws a maze given a map (a 2D array of chars.)
	 * 
	 * Also draws in the ghosts and pacman at their current location.
	 * 
	 * @param g
	 *            The graphics object.
	 */
	private void drawMaze(Graphics g) {
		Graphics2D g2 = (Graphics2D) g;
		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON);
		g2.setColor(Color.BLACK);
		g2.fillRect(0, 0, 500, 500);

		// Could just use scaling, it will cut down code.
		for (int y = 0; y < 21; y++) {
			for (int x = 0; x < 21; x++) {
				if (map[y][x] == 'w') {
					Color color = new Color(51, 102, 255);
					g2.setColor(color);
					g2.fillRect(x * size, y * size, size, size);
				} else if (map[y][x] == 't') {
					g2.setColor(Color.WHITE);
					g2.fillOval((x * size) + (size / 4), (y * size)
							+ (size / 4), size / 2, size / 2);
				} else if (map[y][x] == 'p') {
					g2.setColor(Color.CYAN);
					g2.fillOval((x * size) + (size / 8), (y * size)
							+ (size / 6), 18, 18);
				}
			}
		}
		g2.setColor(Color.YELLOW);
		pacman.drawPacMan(g2, px, py, this, dir, mouth);
		for (int i = 0; i < 4; i++) {
			ghost.drawGhost(g2, gx[i], gy[i], this);
		}

	}

	public int getBoardSize() {
		return size;
	}

	public char[][] getMap() {
		return map;
	}

}
